﻿using System.Runtime.Serialization;
using System.Linq;
using System.Linq.Expressions;
using System;
using System.Reflection;

namespace Ops.Mpc.ExampleApplication.Web.BusinessObjects.Filters
{
    [DataContract(Namespace = "Ops.Mpc.Filters")]
    public class SortFilter : Filter
    {
        #region Fields & Properties
        [DataMember]
        public string PropertyName
        {
            get;
            set;
        }

        [DataMember]
        public bool SortAscending
        {
            get;
            set;
        }
        #endregion

        #region Methods
        public override IQueryable<T> Apply<T>(IQueryable<T> iQueryable)
        {
            if (this.SortAscending)
                return Apply<T>(iQueryable, this.PropertyName, "OrderBy");
            else
                return Apply<T>(iQueryable, this.PropertyName, "OrderByDescending");
        }

        static IQueryable<T> Apply<T>(IQueryable<T> iQueryable, string property, string methodName)
        {         
            string[] props = property.Split('.');         
            Type type = typeof(T);
            ParameterExpression parameterExpression = Expression.Parameter(type, "x");
            Expression expression = parameterExpression;         
            
            foreach(string prop in props) 
            {             
                // use reflection (not ComponentModel) to mirror LINQ            
                PropertyInfo pi = type.GetProperty(prop);
                expression = Expression.Property(expression, pi);             
                type = pi.PropertyType;         
            }         
            
            Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
            LambdaExpression lambda = Expression.Lambda(delegateType, expression, parameterExpression);
            return (IQueryable<T>) typeof(Queryable).GetMethods().Single(method => method.Name == methodName && method.IsGenericMethodDefinition && method.GetGenericArguments().Length == 2 && method.GetParameters().Length == 2).MakeGenericMethod(typeof(T), type).Invoke(null, new object[] { iQueryable, lambda });

        } 

        #endregion
    }
}
